package server

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/elastic/go-elasticsearch/v7/esapi"
	"github.com/google/uuid"
	"kill/common/cron"
	"kill/common/models/es"
	"kill/common/models/mysql"
	"kill/common/models/redis"
	"kill/common/pkg"
	"kill/common/proto/goods"
	"log"
	"strconv"
	"sync"
	"time"
)

func AddKill(in *goods.AddKillReq) (*goods.AddKillResp, error) {
	var good *mysql.Goods
	err := mysql.DB.Where("id=?", in.Id).Find(&good).Error
	if err != nil {
		return nil, errors.New("该商品不存在")
	}
	if good.GoodsNum < int(in.GoodsNum) {
		return nil, errors.New("总库存不足")
	}
	itoa := strconv.Itoa(int(good.ID))
	key := "miaosha" + itoa
	result, err := redis.Rdb.SetNX(context.Background(), key+"setnx", itoa, time.Minute*10).Result()
	if err != nil {
		return nil, errors.New("上锁失败")
	}
	if !result {
		return nil, errors.New("商品预热中")
	}
	defer func() {
		redis.Rdb.Del(context.Background(), key+"setnx")
	}()

	tx := mysql.DB
	tx.Begin()
	killgood := &mysql.KillGoods{
		GoodsName:     good.GoodsName,
		OriginalPrice: good.GoodsPrice,
		GoodsNum:      int(in.GoodsNum),
		CurrentPrice:  float64(in.CurrentPrice),
		StartTime:     in.StartTime,
		EndTime:       in.EndTime,
	}
	err = mysql.DB.Create(&killgood).Error
	if err != nil {
		fmt.Println("预热失败")
		tx.Rollback()
	}

	count := good.GoodsNum - int(in.GoodsNum)
	err = mysql.DB.Model(&good).Update("goods_num", count).Error
	if err != nil {
		fmt.Println("库存修改失败")
		tx.Rollback()
		return nil, errors.New("库存修改失败")
	}
	tx.Commit()

	for i := 0; i < int(in.GoodsNum); i++ {
		redis.GoodsLPush(key, itoa)
	}

	return &goods.AddKillResp{Greet: "预热成功"}, nil
}

func EsToKill(in *goods.EsToKillReq) (*goods.EsToKillResp, error) {

	var (
		list []*mysql.KillGoods
		wg   sync.WaitGroup
	)
	mysql.DB.Find(&list)
	for _, good := range list {
		wg.Add(1)

		go func(good *mysql.KillGoods) {
			defer wg.Done()

			// Build the request body.
			data, err := json.Marshal(good)
			if err != nil {
				log.Fatalf("Error marshaling document: %s", err)
			}

			// Set up the request object.
			req := esapi.IndexRequest{
				Index:      "good",
				DocumentID: strconv.Itoa(int(good.ID)),
				Body:       bytes.NewReader(data),
				Refresh:    "true",
			}

			// Perform the request with the client.
			res, err := req.Do(context.Background(), es.Es)
			if err != nil {
				log.Fatalf("Error getting response: %s", err)
			}
			defer res.Body.Close()
		}(good)
	}
	wg.Wait()
	return &goods.EsToKillResp{}, nil
}
func KillSearch(in *goods.KillSearchReq) (*goods.KillSearchResp, error) {
	var buf bytes.Buffer
	query := map[string]interface{}{}
	if in.GoodsName == "" {
		query = map[string]interface{}{
			"query": map[string]interface{}{
				"match_all": map[string]interface{}{},
			},
		}
	} else {
		query = map[string]interface{}{
			"query": map[string]interface{}{
				"match": map[string]interface{}{
					"goods_name": in.GoodsName,
				},
			},
			"highlight": map[string]interface{}{
				"fields": map[string]interface{}{
					"goods_name": map[string]interface{}{},
				},
			},
		}
	}

	if err := json.NewEncoder(&buf).Encode(query); err != nil {
		log.Fatalf("Error encoding query: %s", err)
	}

	// Perform the search request.
	res, err := es.Es.Search(
		es.Es.Search.WithContext(context.Background()),
		es.Es.Search.WithIndex("good"),
		es.Es.Search.WithBody(&buf),
		es.Es.Search.WithTrackTotalHits(true),
		es.Es.Search.WithPretty(),
	)
	if err != nil {
		log.Fatalf("Error getting response: %s", err)
	}
	defer res.Body.Close()

	var r map[string]interface{}

	json.NewDecoder(res.Body).Decode(&r)
	fmt.Println(r)
	var goodsList []*goods.KillList
	// Print the ID and document source for each hit.
	if in.GoodsName == "" {
		for _, hit := range r["hits"].(map[string]interface{})["hits"].([]interface{}) {
			source := hit.(map[string]interface{})["_source"].(map[string]interface{})
			goodsList = append(goodsList, &goods.KillList{
				Id:            int64(source["ID"].(float64)),
				GoodsName:     source["goods_name"].(string),
				GoodsNum:      int64(source["goods_num"].(float64)),
				CurrentPrice:  float32(source["current_price"].(float64)),
				OriginalPrice: float32(source["original_price"].(float64)),
				StartTime:     source["start_time"].(string),
				EndTime:       source["end_time"].(string),
				Status:        int64(source["status"].(float64)),
			})
		}
		return &goods.KillSearchResp{KillList: goodsList}, nil
	}
	for _, hit := range r["hits"].(map[string]interface{})["hits"].([]interface{}) {
		source := hit.(map[string]interface{})["_source"].(map[string]interface{})
		high := hit.(map[string]interface{})["highlight"].(map[string]interface{})

		goodsList = append(goodsList, &goods.KillList{
			Id:            int64(source["ID"].(float64)),
			GoodsName:     source["goods_name"].(string),
			GoodsNum:      int64(source["goods_num"].(float64)),
			CurrentPrice:  float32(source["current_price"].(float64)),
			OriginalPrice: float32(source["original_price"].(float64)),
			StartTime:     source["start_time"].(string),
			EndTime:       source["end_time"].(string),
			Status:        int64(source["status"].(float64)),
			Highlight:     high["goods_name"].([]interface{})[0].(string),
		})
	}
	return &goods.KillSearchResp{KillList: goodsList}, nil
}
func Register(in *goods.RegisterReq) (*goods.RegisterResp, error) {
	u := mysql.User{}
	username, err := u.FindUserByUsername(in.Username)
	if err != nil {
		return nil, errors.New("查询失败")
	}
	if username.ID != 0 {
		return nil, errors.New("该用户名已注册")
	}
	u = mysql.User{
		Username: in.Username,
		Password: in.Password,
	}
	err = u.CreateUserUsernameAndPassword()
	if err != nil {
		return nil, errors.New("注册失败")
	}
	return &goods.RegisterResp{Great: "注册成功"}, nil

}

func Login(in *goods.LoginReq) (*goods.LoginResp, error) {
	u := mysql.User{}
	username, err := u.FindUserByUsername(in.Username)
	if err != nil {
		return nil, errors.New("查询失败")
	}
	if username.ID == 0 {
		return nil, errors.New("该用户名未注册")
	}
	if u.Password != pkg.PwdMd5(in.Password) {
		return nil, errors.New("密码错误")
	}
	claims := pkg.CustomClaims{ID: u.ID}
	token, err := pkg.NewJWT("2209A").CreateToken(claims)
	if err != nil {
		return nil, errors.New("token生成失败")
	}
	return &goods.LoginResp{Great: token}, nil
}
func RealName(in *goods.RealNameReq) (*goods.RealNameResp, error) {
	ur := mysql.RealName{}
	err := ur.FindRealNameByNameAndIdCard(in.UserId, in.IdCard)
	if err != nil {
		return nil, errors.New("实名失败1")
	}
	if !pkg.RealName(in.Name, in.IdCard) {
		return nil, errors.New("实名失败2")
	}
	date := pkg.GetBirthDate(in.IdCard)
	DateTime, err := time.Parse("20060102", date)
	if err != nil {
		return nil, errors.New("解析失败")
	}
	nowtime := time.Now()
	age := nowtime.Year() - DateTime.Year()
	if nowtime.Month() < DateTime.Month() || (nowtime.Month() == DateTime.Month() && nowtime.Day() < DateTime.Day()) {
		age--
	}
	if age < 18 {
		return nil, errors.New("未成年")
	}
	ur = mysql.RealName{
		UserId: in.UserId,
		Name:   in.Name,
		IdCard: in.IdCard,
	}
	err = ur.CreateRealNameByNameAndIdCard()
	if err != nil {
		return nil, errors.New("实名失败3")
	}
	return &goods.RealNameResp{Great: "实名成功"}, nil
}
func CreateOrder(in *goods.CreateOrderReq) (*goods.CreateOrderResp, error) {
	o := &mysql.Order{}
	g := &mysql.Goods{}
	id, err := g.FindGoodsByGoodsId(in.GoodsId)
	if err != nil {
		log.Println(err)
		return nil, errors.New("查询失败")
	}
	if id.ID == 0 {
		log.Println("没有该商品")
		return nil, errors.New("没有该商品")
	}
	orderId := uuid.New().String()
	o = &mysql.Order{
		UserId:  in.UserId,
		GoodsId: in.GoodsId,
		Num:     in.Num,
		OrderId: orderId,
	}
	tx := mysql.DB
	tx.Begin()
	err = o.CreateOrderByUserIdAndGoodsId()
	if err != nil {
		log.Println(err)
		return nil, errors.New("创建失败")
	}
	count := g.GoodsNum - int(in.Num)
	err = mysql.DB.Model(&g).Update("goods_num", count).Error
	if err != nil {
		fmt.Println("库存修改失败")
		tx.Rollback()
		return nil, errors.New("库存修改失败")
	}
	tx.Commit()

	pay := pkg.NewAliPay()
	price := strconv.FormatFloat(float64(in.Num)*g.GoodsPrice, 'f', 1, 64)
	s := pay.Pay(g.GoodsName, orderId, price)
	pays := fmt.Sprintf("创建成功,请点击链接前往支付%s", s)
	cron.GetOrderStatus(orderId)
	return &goods.CreateOrderResp{Great: pays}, nil
}

func AliPay(in *goods.AliPayReq) (*goods.AliPayResp, error) {
	return &goods.AliPayResp{}, nil
}
